package com.dx.gate.server;

import com.dx.chat.kafka.config.service.IKafkaProducerService;
import com.dx.gate.service.ImWorkerManager;
import com.dx.mobileimsdk.server.ServerLauncher;
import com.dx.mobileimsdk.server.event.MessageQoSEventListenerS2C;
import com.dx.mobileimsdk.server.event.ServerEventListener;
import com.dx.mobileimsdk.server.network.Gateway;
import com.dx.mobileimsdk.server.network.GatewayTCP;
import com.dx.mobileimsdk.server.network.GatewayWebsocket;
import com.dx.mobileimsdk.server.processor.BridgeProcessor;
import com.dx.mobileimsdk.server.qos.QoS4ReciveDaemonC2S;
import com.dx.mobileimsdk.server.qos.QoS4SendDaemonS2C;
import com.dx.mobileimsdk.server.utils.ServerToolKits;
import com.dx.mobileimsdk.server.utils.ServerToolKits.SenseModeTCP;
import io.netty.handler.ssl.ClientAuth;
import io.netty.handler.ssl.SslContext;
import io.netty.handler.ssl.SslContextBuilder;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;

import java.io.IOException;
import java.io.InputStream;

/**
 * IM服务启动入口类。
 */
@Slf4j
@Component
public class ChatServerLauncher extends ServerLauncher implements ApplicationContextAware {
    private static ChatServerLauncher instance = null;

    private static int IM_SERVER_TCP_LISTENNING_PORT;
    private static boolean IM_SERVER_TCP_SUPPORT = true;
    private static IKafkaProducerService iKafkaProducerService;
    private static ImWorkerManager imWorkerManager;


    public ChatServerLauncher() throws IOException {
        super();
    }

    public static ChatServerLauncher getInstance() throws IOException {
        if (instance == null) {
            // 设置TCP网络监听端口（如果需修改此端口号，请前往配置文件中修改）
            GatewayTCP.PORT = IM_SERVER_TCP_LISTENNING_PORT;
            GatewayWebsocket.PORT = IM_SERVER_TCP_LISTENNING_PORT + 1;

            // 设置服务端支持UDP、TCP协议情况（默认同时支持两种，如需修改请前往配置文件中修改）
            boolean isSupportTCP = IM_SERVER_TCP_SUPPORT;

            ServerLauncher.supportedGateways = (isSupportTCP ? Gateway.SOCKET_TYPE_TCP : 0) | (isSupportTCP ? GatewayWebsocket.SOCKET_TYPE_WEBSOCKET : 0);
            ServerLauncher.clusterEnabled = true;
            instance = new ChatServerLauncher();

            // 开启或关闭Debug日志输出
            QoS4SendDaemonS2C.getInstance().setDebugable(false);
            QoS4ReciveDaemonC2S.getInstance().setDebugable(false);

            // 与客户端协商一致的心跳频率模式设置
            ServerToolKits.setSenseModeTCP(SenseModeTCP.MODE_5S);

            // 设置最大TCP帧内容长度（不设置则默认最大是 6 * 1024字节）
//			GatewayTCP.TCP_FRAME_MAX_BODY_LENGTH = 60 * 1024;

            // 开启TCP协议的SSL/TLS加密传输（请确保客户端也已开发SSL）
//			GatewayTCP.sslContext = createSslContext();

            // 关闭与Web端的消息互通桥接器 (TODO：如需与Web端IM互通，请配置此项为ture，否则请设为false)
            ServerLauncher.bridgeEnabled = false;
            // 跨服桥接器MQ的URI（本参数只在ServerLauncher.bridgeEnabled为true时有意义）
            BridgeProcessor.IMMQ_URI = "amqp://js:19844713@192.168.99.206";
        }
        return instance;
    }

    /**
     * 创建SslContext对象，用于开启SSL/TLS加密传输。
     *
     * @return 如果成功创建则返回SslContext对象，否则返回null
     */
    private static SslContext createSslContext() {
        try {
            /** 示例 1：使用证书（证书位于绝对路径）*/
//			// 证书文件
//			File certChainFile = new File("c:/certs/netty-cert2.crt");
//			// 证书文件
//			File keyFile = new File("c:/certs/netty-key2.pk8");
//			// 私钥密码（注意：Netty只支持.pk8格式，如何生成，见JackJiang文章：）
//			String keyPassword = "123456";
//			// 生成SslContext对象（为了方便理解，此处使用的是单向认证）
//			SslContext sslCtx = SslContextBuilder.forServer(certChainFile, keyFile, keyPassword).clientAuth(ClientAuth.NONE).build();

            /** 示例 2：使用证书（证书位于相对路径）*/
            // TODO: 注意：请使用自已的证书，Demo中带的证书为自签名证书且已绑定域名，不安全！！！
            // 证书文件
            InputStream certChainFile = ChatServerLauncher.class.getResourceAsStream("certs/netty-cert2.crt");
            // 私钥文件（注意：Netty只支持.pk8格式，如何生成，见JackJiang文章：）
            InputStream keyFile = ChatServerLauncher.class.getResourceAsStream("certs/netty-key2.pk8");
            // 私钥密码（注意：Netty只支持.pk8格式，如何生成，见JackJiang文章：）
            String keyPassword = "123456";
            // 生成SslContext对象（为了方便理解，此处使用的是单向认证）
            SslContext sslCtx = SslContextBuilder.forServer(certChainFile, keyFile, keyPassword).clientAuth(ClientAuth.NONE).build();

            /** 示例 3：使用Netty自带的自签名证书（建议该证书仅用于测试使用）*/
//			SelfSignedCertificate ssc = new SelfSignedCertificate();
//			SslContext sslCtx = SslContextBuilder.forServer(ssc.certificate(), ssc.privateKey()).build();

            return sslCtx;
        } catch (Exception e) {
            log.warn("createSslContext()时出错了，原因：" + e.getMessage(), e);
        }

        return null;
    }

    @Value("${netty.port}")
    public void setImServerTcpListenningPort(int imServerTcpListenningPort) {
        IM_SERVER_TCP_LISTENNING_PORT = imServerTcpListenningPort;
    }

    /**
     * 初始化回调处理事件监听器。
     * <p>
     * 本类重写 {@link #initListeners()}方法，以实现应用层的回调监听器以便实现自定义业务
     * 逻辑，可以设置的回调监听器有： {@link #setServerEventListener(ServerEventListener)}
     * 和 {@link #setServerMessageQoSEventListener(MessageQoSEventListenerS2C)}。
     */
    @Override
    protected void initListeners() {
        //kafkaTemplate
        // ** 设置回调
        this.setServerEventListener(new ChatServerEventListener(iKafkaProducerService, imWorkerManager));
        this.setServerMessageQoSEventListener(new MessageQoSEventListner());
    }

    /**
     * 重写父类的本方法，方便直接使用本工程中的ChatServerEventListener（即ServerEventListener
     * 接口的实现类。
     * <p>
     * {@inheritDoc}
     */
    @Override
    public ChatServerEventListener getServerEventListener() {
        return (ChatServerEventListener) serverCoreHandler.getServerEventListener();
    }

    @Override
    public void setApplicationContext(ApplicationContext context) throws BeansException {
        this.iKafkaProducerService = context.getBean("iKafkaProducerService", IKafkaProducerService.class);
        this.imWorkerManager = context.getBean("imWorkerManager", ImWorkerManager.class);

    }
}
